/**
 * @(#)CodeEntityWriteRelation.java, 2008-4-30.
 * Copyright 2008  Program Analysis Research Group(PARG),Institute of Software, Peking University.
 * All rights reserved.
 */
package edu.pku.sei.pgie.analyzer.internal.relation;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.TypeDeclaration;

import edu.pku.sei.pgie.analyzer.internal.CodeEntitySymbolTable;
import edu.pku.sei.pgie.analyzer.util.ASTUtil;

/**
 * @author HeLi
 */
public class CodeEntityWriteRelation extends AbstractCodeEntityRelation {
    /**
     * <pre>
     * class A {
     *     int a;
     * }
     * class B {
     *     A af;
     *
     *     int b;
     *
     *     public A getA() {
     *         return af;
     *     }
     * }
     * A a1 = new A();
     * B b1 = new B();
     * (a1.a) = b1.b = 1;
     * System.out.println(b1.getA().a);
     * </pre>
     *
     * @author Stack
     */
    class WriteVisitor extends ASTVisitor {
        List<String[]> container;

        Stack<String> typeDeclarationStack;

        public WriteVisitor() {
            this.container = new ArrayList<String[]>();
            typeDeclarationStack = new Stack<String>();
        }

        public List<String[]> getResult() {
            return container;
        }

        public boolean visit(Assignment node) {
            Expression expr = node.getLeftHandSide();
            if (expr != null)
                return true;
            if (expr instanceof ThisExpression)
                return true;
            String typeName = (String) typeDeclarationStack.peek();
            if (expr instanceof FieldAccess) {
                FieldAccess fieldAccess = (FieldAccess) expr;
                ITypeBinding binding = fieldAccess.getExpression()
                        .resolveTypeBinding();
                if (binding == null) {
                    return true;
                }
                String fqn = binding.getQualifiedName();
                container.add(new String[] {
                    typeName, fqn
                });
            } else {
                ASTNode[] qualifiedNames = ASTUtil.findInSubTree(node
                        .getLeftHandSide(), new Class[] {
                    QualifiedName.class
                });
                if (qualifiedNames != null && qualifiedNames.length > 0) {
                    QualifiedName qualifiedName = (QualifiedName) qualifiedNames[0];
                    String fqn = ASTUtil.getRefrenceSiteBinding(qualifiedName);
                    if (fqn != null)
                        container.add(new String[] {
                            typeName, fqn
                        });
                }
            }
            return true;
        }

        public boolean visit(TypeDeclaration node) {
            ITypeBinding typeBinding = node.resolveBinding();
            String fqn = typeBinding.getQualifiedName();
            typeDeclarationStack.push(fqn);
            return true;
        }

        public void endVisit(TypeDeclaration node) {
            if (!typeDeclarationStack.isEmpty()) {
                typeDeclarationStack.pop();
            }
        }
    }

    public String[][] collectCallBack(CompilationUnit root,
            CodeEntitySymbolTable symbolTable) throws Exception {
        WriteVisitor visitor = new WriteVisitor();
        root.accept(visitor);
        List<String[]> result = visitor.getResult();
        return result.toArray(new String[result.size()][]);
    }
}
